/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.esdc.librarysystem.mdb;

import com.esdc.librarysystem.entity.FineEntity;
import com.esdc.librarysystem.entity.MemberEntity;
import com.esdc.librarysystem.entity.PaymentEntity;
import com.esdc.librarysystem.exception.NotExistException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 *
 * @author shou1dwe
 */
@MessageDriven(mappedName = "jms/PaymentQueue", activationConfig = {
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class PaymentBean implements MessageListener {

    private static final int RETRIEVE_OUTSTANDING_FINE = 0;
    private static final int PAY_FINE = 1;
    @PersistenceContext
    private EntityManager entityManager;
    @Resource(mappedName = "jms/testqfc")
    private ConnectionFactory queueConnectionFactory;

    public PaymentBean() {
    }

    public void onMessage(Message message) {
        try {
            if (message instanceof MapMessage) {
                print("Message received at PaymentBean.");
                processRequest((MapMessage) message);
            }

        } catch (JMSException ex) {
            Logger.getLogger(PaymentBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void processRequest(MapMessage mapMessage) throws JMSException {
        Map replyMessage = new HashMap<String, Object>();
        switch (mapMessage.getInt("RequestType")) {
            case RETRIEVE_OUTSTANDING_FINE:
                try {
                    Double fine = retrieveFine(mapMessage.getLong("Id"));
                    replyMessage.put("ResponseType", RETRIEVE_OUTSTANDING_FINE);
                    replyMessage.put("Fine", fine);
                }catch (NotExistException ex) {
                    replyMessage.put("Error", ex.getMessage());
                    Logger.getLogger(PaymentBean.class.getName()).log(Level.SEVERE, null, ex);
                }
                reply(mapMessage, replyMessage);
                break;
            case PAY_FINE:
                try {
                    Double fine = payFine(mapMessage.getLong("Id"), mapMessage.getDouble("Amount"));
                    replyMessage.put("ResponseType", PAY_FINE);
                    replyMessage.put("Fine", fine);
                    reply(mapMessage, replyMessage);
                    break;
                } catch (NotExistException ex) {
                    replyMessage.put("Error", ex.getMessage());
                    Logger.getLogger(PaymentBean.class.getName()).log(Level.SEVERE, null, ex);
                }
            default:
                break;
        }
    }

    private void reply(MapMessage mapMessage, Map replyMessage) {
        Connection connection = null;
        try {
            connection = queueConnectionFactory.createConnection();
        } catch (JMSException ex) {
            Logger.getLogger(PaymentBean.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Destination replyDestination = mapMessage.getJMSReplyTo();
            String replyMessageId = mapMessage.getJMSCorrelationID();
            print("request message ID " + mapMessage.getJMSMessageID());
            MessageProducer producer = session.createProducer(replyDestination);
            producer.send(transformToMapMessage(session, replyMessageId, replyMessage));
        } catch (JMSException ex) {
            Logger.getLogger(PaymentBean.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (connection!=null){
                try {
                    connection.close();
                    print("connection closed");
                } catch (JMSException ex) {
                    Logger.getLogger(PaymentBean.class.getName()).log(Level.SEVERE, null, ex);
                }
                connection = null;
            }
        }

    }

    private void print(String string) {
        System.out.println("[PaymentBean] " + string);
    }

    private Double retrieveFine(long id) throws NotExistException {
        MemberEntity member = entityManager.find(MemberEntity.class, id);
        if (member == null) {
            throw new NotExistException("FinePaymentMDB:retrieveFine Member not exist");
        }
        entityManager.refresh(member);
        return member.getFine() == null ? 0 : member.getFine().getOwing();
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    private Double payFine(long id, double amount) throws NotExistException {
        MemberEntity member = entityManager.find(MemberEntity.class, id);
        if (member == null) {
            throw new NotExistException("FinePaymentMDB:retrieveFine Member not exist");
        }
        PaymentEntity paymentEntity = new PaymentEntity();
        paymentEntity.create(amount, new Date());
        entityManager.persist(paymentEntity);
        FineEntity fineEntity = member.getFine();
        fineEntity.newPayment(paymentEntity);
        fineEntity.setOwing(fineEntity.getOwing() - amount);
        entityManager.flush();
        entityManager.refresh(fineEntity);
        return fineEntity.getOwing();
    }

    private Message transformToMapMessage(Session session, String messageId, Map replyMap) throws JMSException {
        MapMessage replyMessage = session.createMapMessage();
        for (Object entry : replyMap.entrySet()) {
            Map.Entry message = (Map.Entry<String, Object>) entry;
            replyMessage.setObject((String) message.getKey(), message.getValue());
        }
        replyMessage.setJMSCorrelationID(messageId);
        return replyMessage;
    }
}
